div
<script setup lang="ts">
import { ColumnsType } from "ant-design-vue/es/table";
import { h } from "vue";
const antIcons: any = inject("antIcons");
import {
  getAllRole, AllRole, roleUsers, RoleUsersRes, removeRoleUsersApi,
  RemoveRoleUsersRes, getUserNameLike, addRoleUsersApi, getMenuTree, GetMenuTreeRes, roleAddAuth,
  roleAuthAll, addRoleApi, AddRoleRes
} from "@/api/security";
import { SysMenuTree, SysRole, SysUser } from "@/interface/user";
import {
  deleteUser,
  updateRole,
  UpdateRoleApi,
  deleteRoles,
  RoleAddAuthRes,
  RoleAuthAllRes
} from "@/api/security";
import { message } from "ant-design-vue";
import { CheckOutlined, CloseOutlined } from "@ant-design/icons-vue";
import { Key } from "ant-design-vue/es/table/interface";
import dayjs, { Dayjs } from "dayjs";
import { DefaultOptionType } from "ant-design-vue/es/vc-cascader";
import {
  LoadingOutlined
} from '@ant-design/icons-vue';
import { DataNode } from "ant-design-vue/es/tree";
import { theme } from 'ant-design-vue';
import { useGlobalStore } from "@/store/globalStore";


const { useToken } = theme;
const { token } = useToken();
// colorPrimary
const globalStore = useGlobalStore()
const columns: ColumnsType<any> = [
  {
    title: "角色名称",
    dataIndex: "roleName",
    key: "roleName",
  },
  {
    title: "角色权限符",
    dataIndex: "roleKey",
    key: "roleKey",
  },
  {
    title: "创造时间",
    dataIndex: "createTime",
    key: "createTime",
  },
  {
    title: "状态",
    dataIndex: "status",
    key: "status",
  },
  {
    title: "操作",
    key: "operation",
    fixed: "right",
    width: 150,
  },
];

const data = ref<SysUser[]>([]);

const contentBoxRef = ref<HTMLElement>();
const currentPage = ref<number>(1);
const size = ref<number>(10);
const total = ref<number>(0);
const isLoading = ref<boolean>(false);
const editApiLoading = ref<boolean>(false);
const editPopShow = ref<boolean>(false);
const searchValue = ref("");

const currEditDate = ref<Dayjs>();
const currEditData = reactive<SysRole>({
  roleId: -1,
  roleName: "",
  roleKey: "",
  createTime: "",
  status: "",
});

const tableStyle = reactive<{
  width: number;
  height: number;
}>({
  width: 300,
  height: 300,
});

const getRoles = async (isSearch = false) => {
  isLoading.value = true;
  data.value = [];
  if (isSearch) {
    pageChanged(1, size.value);
    return;
  }
  try {
    const { data: res }: { data: AllRole } = await getAllRole(
      currentPage.value,
      size.value,
      searchValue.value
    );
    currentPage.value = res.data.current;
    size.value = res.data.size;
    total.value = res.data.total;
    //key用于选中操作
    data.value = res.data.records.map((item: any) => {
      item.key = item.roleId;
      return item;
    });
  } catch (error) {
  } finally {
    isLoading.value = false;
  }
};

const pageChanged = (page: number, pageSize: number) => {
  currentPage.value = page;
  size.value = pageSize;
  getRoles();
};

const reloadData = () => {
  getRoles();
};

const resetData = () => {
  searchValue.value = "";
  pageChanged(1, 10);
};

const deleteData = async (_e: MouseEvent, roleId: number) => {
  return new Promise((resolve) => {
    deleteRoles(roleId.toString()).then((res) => {
      if (res.data.code == 200) {
        message.success("删除成功");
        reloadData();
      } else {
        message.error(res.data.msg || "删除失败");
      }
      resolve(true);
    });
  });
};

const deleteBatchData = async () => {
  deleteUser(selectedRows.value.join(",")).then((res) => {
    if (res.data.code == 200) {
      message.success("删除成功");
    } else {
      message.error(res.data.msg || "删除失败");
    }
    getRoles();
  });
};

const toggleDataStatus = (
  _checked: boolean | string | number,
  _event: Event
) => { };

//编辑按钮触发
const handleEditOk = async () => {
  editApiLoading.value = true;
  try {
    Promise.all([updateRole({
      ...currEditData,
      createTime:
        currEditDate.value?.format("YYYY-MM-DD HH:mm:ss") ||
        currEditData.createTime,
    }), roleAddAuth(currEditData.roleId as number, selectedPermKeys.value)]).then((result: any) => {
      const { data: res }: { data: UpdateRoleApi } = result[0]
      if (res.code == 200) {
        message.success("角色信息更新成功");
      } else {
        message.error(res.msg || "更新失败");
      }

      const { data: res2 }: { data: RoleAddAuthRes } = result[1]
      if (res2.code == 200) {
        message.success(res2.msg);
        selectedPermKeys.value = []
      } else {
        message.error(res2.msg || "角色权授失败");
      }
      getRoles();
      editPopShow.value = false;
    })
  } catch (error) {
  } finally {
    editApiLoading.value = false;
  }
};

let firstOpen = true
const chooseMenuList = ref<DataNode[]>([]);
const editData = async (sysRole: any) => {
  editPopShow.value = true;
  const { roleId, roleName, roleKey, createTime, status } = sysRole;
  currEditData.roleId = roleId;
  currEditData.roleName = roleName;
  currEditData.roleKey = roleKey;
  currEditData.createTime = createTime;
  currEditData.status = status;
  currEditDate.value = dayjs(currEditData.createTime, "YYYY-MM-DD HH:mm:dd");

  //加载用户对应的权限
  roleAuthAll(sysRole.roleId as number).then(({ data: res }: { data: RoleAuthAllRes }) => {
    if (res.code === 200) {
      selectedPermKeys.value = res.data
    }
  })

  if (firstOpen) {
    firstOpen = false
    const { data: res }: { data: GetMenuTreeRes<SysMenuTree[]> } = await getMenuTree()

    //是目录用自增key
    let myKey: number = 1
    // 递归处理菜单
    const dfs = (menu: SysMenuTree) => {
      if (menu.menuType === 'M') {
        myKey++
      }
      let cur: DataNode = {
        key: (menu.menuPermPrefix && menu.menuPermPrefix !== "") ? menu.menuPermPrefix : myKey.toString(),
        name: menu.menuName,
        children: [] as DataNode[],
        icon: menu.icon
      }
      if (menu.menuType === 'B') {
        cur.children = [
          {
            name: '查询',
            key: menu.menuPermPrefix + ':query',
          },
          {
            name: '新增',
            key: menu.menuPermPrefix + ':add',
          },
          {
            name: '更新',
            key: menu.menuPermPrefix + ':update',
          },
          {
            name: '删除',
            key: menu.menuPermPrefix + ':delete',
          },
        ]
        return cur
      }
      menu.children?.forEach(nextMenu => {
        cur.children?.push(dfs(nextMenu) as DataNode)
      })
      return cur
    }
    res.data.forEach(menu => {
      chooseMenuList.value.push(dfs(menu) as DataNode)
    })
  }
};

const onFinishFailed = (errorInfo: any) => {
  console.log("Failed:", errorInfo);
};

//当前选中行
const selectedRows = ref<Key[]>([]);
const onSelectChange = (selectedRowKeys: Key[]) => {
  selectedRows.value = selectedRowKeys;
};

const tabKey = ref("1");

const role2Users = ref<SysRole[]>([]);
const getRoleAboutUsers = async () => {
  const { data: res }: { data: RoleUsersRes } = await roleUsers();
  role2Users.value = res.data;
};

const RoleRemoveUser = async (userId: number | undefined, roleId: number | undefined) => {
  const { data: res }: { data: RemoveRoleUsersRes } = await removeRoleUsersApi(userId, roleId)
  if (res.code === 200) {
    message.success(res.msg);
    getRoleAboutUsers();
  } else {
    message.error(res.msg);
  }
}

//弹出modal模块
const addRolePop = ref(false)
let firstLoading = false
const currEditRole = ref<SysRole>({})
const searchContent = ref('')
const userOptions = ref<SysUser[]>([])
const userList = ref<SysUser[]>([])
const userLoading = ref(false)
const selectTags = ref<boolean[]>([])
const addRole = () => {
  const selectedUserIds: number[] = []
  selectTags.value.forEach((selected: boolean, idx: number) => {
    if (selected) {
      selectedUserIds.push(userList.value[idx].userId as number)
    }
  })
  addRoleUsersApi(selectedUserIds.join(','), currEditRole.value.roleId as number).then(res => {
    if (res.data.code === 200) {
      message.success(res.data.msg)
      addRolePop.value = false
      getRoleAboutUsers()
    } else {
      message.error(res.data.msg)
    }
  })
}
const openAddRolePop = (role: SysRole) => {
  addRolePop.value = true
  currEditRole.value = role
  if (!firstLoading) {
    userLoading.value = true
    firstLoading = true
    getUserNameLike('').then(res => {
      userList.value = res.data.data
      selectTags.value = Array(res.data.data.length).fill(false)
    }).finally(() => userLoading.value = false)
  } else {
    selectTags.value = Array(userList.value.length).fill(false)
  }
}
let key: NodeJS.Timeout | null = null
const searchContentChange = (value: any, _option: DefaultOptionType | DefaultOptionType[]) => {
  searchContent.value = value
  clearTimeout(key as NodeJS.Timeout)
  userLoading.value = true
  key = setTimeout(() => {
    getUserNameLike(searchContent.value).then(res => {
      userList.value = res.data.data
      selectTags.value = Array(res.data.data.length).fill(false)
    }).finally(() => userLoading.value = false)
  }, 200)
}

//分配权限相关
const selectedPermKeys = ref<string[]>([])

//新增相关
const currAddData = ref<SysRole>({
  roleName: '',
  roleKey: '',
})

const addPopShow = ref(false)
const addApiLoading = ref(false)

const handleAddOk = async () => {
  try {
    addApiLoading.value = true
    const { data: res }: { data: AddRoleRes } = await addRoleApi(currAddData.value)
    if (res.code === 200) {
      message.success('新增成功')
      getRoles()
      addPopShow.value = false
    }
  } catch (error) {

  } finally {
    addApiLoading.value = false
  }
}

const onAddFinishFailed = (errorInfo: any) => {
  console.log('Failed:', errorInfo);
};

const handleAddData = () => {
  addPopShow.value = true
}

onMounted(() => {
  getRoles();
  getRoleAboutUsers();
  tableStyle.height = (contentBoxRef.value?.clientHeight as number) - 200;
  tableStyle.width = contentBoxRef.value?.clientWidth as number;
});
</script>
<template>
  <div class="box-border relative h-full p-3" ref="contentBoxRef" :style="{ width: globalStore.isMobile ? 'calc(100% - 60px)' : '100%' }">
    <a-tabs v-model:activeKey="tabKey" type="card">
      <a-tab-pane key="1" tab="角色表">
        <a-row>
          <a-col :span="24">
            <a-space class="flex flex-wrap">
              <a-button type="ghost"
                class="flex items-center text-white bg-green-500 hover:bg-green-400 hover:border-green-400 "
                :icon="h(antIcons['PlusOutlined'])" @click="handleAddData" :loading="isLoading">新增</a-button>
              <a-input placeholder="名称/用户名/性别/电话号码" class="w-[230px]" v-model:value="searchValue"
                @keydown.enter="getRoles(true)" />
              <a-button type="primary" class="flex items-center bg-primary-2" :icon="h(antIcons['SearchOutlined'])"
                @click="getRoles(true)" :loading="isLoading">查询</a-button>
              <a-button class="flex items-center" :icon="h(antIcons['ReloadOutlined'])" @click="resetData">重置</a-button>
              <a-button class="flex items-center justify-center" :icon="h(antIcons['ReloadOutlined'])" shape="circle"
                @click="reloadData"></a-button>
              <a-button type="primary" class="flex items-center" danger :icon="h(antIcons['CloseOutlined'])"
                :disabled="selectedRows.length == 0" @click="deleteBatchData">删除</a-button>
            </a-space>
          </a-col>
        </a-row>
        <!-- 表格 -->
        <div class="mt-3 h-fit max-h-[647px]">
          <a-table :columns="columns" :data-source="data" :pagination="false" :loading="isLoading"
            :scroll="{ y: tableStyle.height, x: globalStore.isMobile ? 'calc(100% + 800px)' : '100%' }" :row-selection="{
      selectedRowKeys: selectedRows,
      onChange: onSelectChange,
    }">
            <template #bodyCell="{ column, text }">
              <template v-if="column.dataIndex === 'sex'">
                <p>{{ text == "1" ? "男" : "女" }}</p>
              </template>
              <template v-if="column.dataIndex === 'status'">
                <a-switch :checked="text === '0'" @click="toggleDataStatus" disabled>
                  <template #checkedChildren>
                    <CheckOutlined />
                  </template>
                  <template #unCheckedChildren>
                    <CloseOutlined />
                  </template>
                </a-switch>
              </template>
              <template v-if="column.key === 'operation'">
                <a-space>
                  <a-button type="primary" class="flex items-center bg-primary-2" size="small"
                    @click="editData(text)">编辑</a-button>
                  <a-popconfirm :title="`确定删除${text.roleName}吗?`" ok-text="确认" cancel-text="取消"
                    :okButtonProps="{ type: 'default' }" @confirm="deleteData($event, text.roleId)">
                    <!-- <template #okButton>
                  <a-button size="small" type="primary" class="bg-primary-2" @click="deleteData(text.userId)">确认</a-button>
                </template> -->
                    <a-button size="small" type="primary" danger class="flex items-center text-white">删除</a-button>
                  </a-popconfirm>
                </a-space>
              </template>
            </template>
          </a-table>
        </div>

        <a-modal v-model:open="editPopShow" title="修改角色信息" :footer="null">
          <a-form :model="currEditData" name="currEditData" @finish="handleEditOk" class="mt-5" place="left"
            @finishFailed="onFinishFailed" v-bind="{
      labelCol: { span: 4 },
      wrapperCol: { span: 24 },
    }">
            <a-form-item name="roleName" label="角色名称" :rules="[{ required: true }]">
              <a-input v-model:value="currEditData.roleName" />
            </a-form-item>
            <a-form-item name="roleKey" label="角色权限符" :rules="[{ required: false }]">
              <a-input v-model:value="currEditData.roleKey" />
            </a-form-item>
            <a-form-item name="createTime" label="创造时间" :rules="[{ required: false }]">
              <a-date-picker v-model:value="currEditDate" format="YYYY-MM-DD HH:mm:ss" show-time />
            </a-form-item>
            <a-form-item name="status" label="状态" :rules="[{ required: false }]">
              <a-switch v-model:checked="currEditData.status" checkedValue="0" unCheckedValue="1">
                <template #checkedChildren>
                  <CheckOutlined />
                </template>
                <template #unCheckedChildren>
                  <CloseOutlined />
                </template>
              </a-switch>
            </a-form-item>
            <a-form-item label="菜单权限" :rules="[{ required: false }]">
              <a-tree v-model:checkedKeys="selectedPermKeys" checkable :tree-data="chooseMenuList" :field-names="{
      children: 'children',
      title: 'menuName'
    }">
                <template #title="{ name }">
                  <span>{{ name }}</span>
                </template>

              </a-tree>
            </a-form-item>
            <a-form-item :wrapper-col="{
      offset: 17,
      span: 7,
    }">
              <a-space>
                <a-button key="back" @click="editPopShow = false">取消</a-button>
                <a-button html-type="submit" type="primary" class="bg-primary-2" :loading="editApiLoading">修改</a-button>
              </a-space>
            </a-form-item>
          </a-form>
        </a-modal>

        <a-modal v-model:open="addPopShow" title="创建角色" :footer="null">
          <a-form :model="currAddData" name="currAddData" @finish="handleAddOk" class="mt-5" place="left"
            @finishFailed="onAddFinishFailed" v-bind="{
      labelCol: { span: 5 },
      wrapperCol: { span: 24 },
    }">
            <a-form-item name="roleName" label="角色名称" :rules="[{ required: true }]">
              <a-input v-model:value="currAddData.roleName" placeholder="角色名称" />
            </a-form-item>
            <a-form-item name="roleKey" label="角色权限符" :rules="[{ required: true }]">
              <a-input v-model:value="currAddData.roleKey" placeholder="角色权限符" />
            </a-form-item>
            <a-form-item :wrapper-col="{
      offset: 17,
      span: 7
    }">
              <a-space>
                <a-button key="back" @click="addPopShow = false">取消</a-button>
                <a-button html-type="submit" type="primary" class="bg-primary-2" :loading="addApiLoading">创建</a-button>
              </a-space>
            </a-form-item>
          </a-form>
        </a-modal>

      </a-tab-pane>
      <a-tab-pane key="2" tab="用户角色映射">
        <div v-for="r2u in role2Users" class="py-3 border-b-[0.1px] first:pt-0">
          <a-tag class="px-2 py-1 dark:text-white" :color="token.colorPrimary" :bordered="false">{{
      r2u.roleName
    }}</a-tag>
          <div class="flex flex-wrap items-center mt-2">
            <a-tag v-for="user in r2u.sysUserList" class="px-2 py-1 transition-all duration-300 group"
              :bordered="false">{{
      user.userName }}

              <a-popconfirm :title="'确认从' + r2u.roleName + '组移除' + user.userName + '用户吗？'" ok-text="Yes"
                cancel-text="No" @confirm="RoleRemoveUser(user.userId, r2u.roleId)">
                <span class="hidden px-1 cursor-pointer thin group-hover:inline-block" v-if="r2u.roleName !== '未分配'">
                  <CloseOutlined />
                </span>
              </a-popconfirm>
            </a-tag>
            <a-tooltip :title="r2u.roleName + '组 添加用户'" v-if="r2u.roleName !== '未分配'">
              <a-button class="flex items-center justify-center" size="small" shape="circle"
                :icon="h(antIcons['PlusOutlined'])" @click="openAddRolePop(r2u)" />
            </a-tooltip>
          </div>
        </div>

        <a-modal v-model:open="addRolePop" :title="`分配角色-${currEditRole.roleName}`" @ok="addRole">
          <p class="py-2">选择要添加的用户</p>
          <a-auto-complete v-model:value="searchContent" :options="userOptions" class="w-full" placeholder="用户名/昵称/id"
            @change="searchContentChange" :okButtonProps="{ type: 'default' }" />
          <div class="mt-2">
            <a-checkable-tag v-for="(user, index) in userList" :key="index" v-model:checked="selectTags[index]">
              {{ user.userName }}
            </a-checkable-tag>
          </div>
          <div class="text-center" v-show="userLoading">
            <LoadingOutlined class="text-[24px]" />
          </div>
        </a-modal>
      </a-tab-pane>
    </a-tabs>

    <div
      class="absolute bottom-0 left-0 right-0 p-3 mt-3 bg-white border-t-[1px] z-10 dark:bg-darkBg dark:border-gray-600"
      v-show="tabKey === '1'">
      <a-pagination v-model:current="currentPage" v-model:page-size="size" :total="total"
        :show-total="(total: number) => `总共 ${total} 条`" show-size-changer @change="pageChanged" />
    </div>
  </div>
</template>

<style scoped></style>
